import { globalConfig } from '../../config/global'

const circularJson = require('circular-json')

/**
 * 日志打印类
 */
class Logger{
  constructor(context){
    this.context = context;
  }

  debug(...args){
    globalDebug(this.context, ...args)
  }

  log(...args){
    globalLog(this.context, ...args)
  }

  warn(...args){
    globalWarn(this.context, ...args)
  }

  error(...args){
    globalError(this.context, ...args)
  }
}

// 创建一个日志环境
export function createLogger(context){
  return new Logger(context);
}

function prepareObjectForLogging(obj, maxDepth = 1){
  // 这个东西不知道是什么东西
  if(!window.Sentry){
    return obj;
  }

  if(typeof obj !== 'object' && !Array.isArray(obj)){
    return obj
  }
  // 存在Sentry的情况下，对象将最多显示深度一层也就是{a: 1, b: {c: 2, d: {e: 3}}} 里面的d只显示[object]
  const result = {};
  for(const key in obj){
    const val = obj[key]

    if(typeof val === 'object'){
      if(maxDepth > 0){
        result[key] = prepareObjectForLogging(val, maxDepth--);
      } else {
        result[key] = '[object]'
      }
    } else {
      result[key] = val;
    }
  }

  return result;
}

function serializeError(err){
  if(!err){
    return null;
  }
  const result = {
    type: err.constructor.name
  }

  // ErroEvent 是文件中报出的错误，将会显示行数列数等信息

  if(err instanceof Error){
    result.message = err.message;
    result.name = err.name;
    result.stack = err.stack;
    result.type = '{type.Error}'
  } else if(err instanceof ErrorEvent){
    result.filname = err.filename;
    result.message = err.message;
    result.linno = err.linno;
    result.colno = err.colno;
    result.type = "{type.ErrorEvent}"

    if(err.error){
      result.error = serializeError(err.error)
    } else {
      result.error = "{not-provided}"
    }
  } else {
    result.type = `{unknown-type:${typeof err}}`
  }

  return result;
}

function serializeEvent(ev){
  let result = {
    type: `{type.Event:"${typeof ev}"}`
  }
  result.eventType = ev.type;
  return result;
}

function preparePayload(key, value){
  if(value instanceof Error || value instanceof ErrorEvent){
    return serializeError(value)
  }
  if(value instanceof Event){
    return serializeEvent(value)
  }
  if(typeof value === 'undefined'){
    return null;
  }
  return value;
}

export function stringifyObjectContainingErrors(payload){
  return circularJson.stringify(payload, preparePayload)
}

export function globalDebug(context, ...args){
  if(G_IS_DEV){
    logInternal(context, console.debug, prepareArgsForLogging(args))
  }
}

export function globalLog(context, ...args){
  logInternal(context, console.log, prepareArgsForLogging(args))
}

export function globalWarn(context, ...args){
  logInternal(context, console.warn, prepareArgsForLogging(args))
}

export function globalError(context, ...args){
  logInternal(context, console.error, prepareArgsForLogging(args))
}

// 将参数转换为合适的数据类型
function prepareArgsForLogging(args){
  let result = [];
  for(let i = 0; i < args.length; ++i){
    result.push(prepareObjectForLogging(args[i]))
  }
  return result;
}

export function logSection(name, color){
  while(name.length <= 14){
    name = " " + name + " "
  }
  name = name.padEnd(19, " ")

  const lineCss = "letter-spacing: -3px; color: " + color + "; font-size: 6px; background: #eee; color: #eee;";
  const line = "%c--------------------";
  console.log("\n" + line + " %c" + name + " " + line + '\n', lineCss, 'color: ' + color, lineCss);
}

// 判断传入的上下文是否是类，如果是就提取其中的name参数
function extractHandleContext(handle){
  let context = handle || 'unknown'
  if(handle && handle.constructor && handle.constructor.name){
    context = handle.constructor.name;
    // 如果本身就是文字，那么就会得到String构造器的name
    if(context === 'String'){
      context = handle;
    }
  }

  // 判断是否是对象，及其中的name
  if(handle && handle.name){
    context = handle.name
  }
  return context + '';
}

// 内部信息
function logInternal(handle, consoleMethod, args){
  const context = extractHandleContext(handle).padEnd(20, ' ');
  const labelColor = handle && handle.LOG_LABEL_COLOR ? handle.LOG_LABEL_COLOR : '#aaa'

  // 开发状态下，是否启用时间戳查看性能
  if(G_IS_DEV && globalConfig.debug.logTimestamps){
    const timestamp = "⏱ %c" + (Math.floor(performance.now()) + '').padEnd(6, ' ') + '';
    consoleMethod.call(
      console,
      timestamp + ' %c' + context,
      'color: #f00;',
      'color: ' + labelColor + ';',
      ...args
    );
  } else {
    consoleMethod.call(console, '%c' + context, 'color: ' + labelColor, ...args);
  }
}